package org.summer.generator.writer.impl;

import org.summer.generator.StringUtil;
import org.summer.generator.handler.FileCreateHandler;
import org.summer.generator.log.Logger;
import org.summer.generator.log.LoggerManager;
import org.summer.generator.vo.Column;
import org.summer.generator.vo.MethodInfo;
import org.summer.generator.writer.AbstractFileWriter;
import org.summer.generator.writer.WriterToResource;

import java.util.List;

/**
 * mybatis Mapper接口XML文件写入处理类
 */
@WriterToResource(suffix = "Mapper", extension = FileCreateHandler.XML_FILE_EXTENSION)
public class MapperXmlFileWriter extends AbstractFileWriter {
    private static final Logger log = LoggerManager.getLogger(MapperXmlFileWriter.class);
    @Override
    public void execute() {
        channel.writerNewline("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
        channel.writerNewline("<!DOCTYPE mapper\n" +
                "        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n" +
                "        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">");
        channel.writerNewline("<mapper namespace=\"%s\">".formatted(getNamespace()));
        channel.writerEmptyLine();
        List<MethodInfo> mds = super.parseMethodDefinitions(writer.getMapperParentInterface());
        mds.forEach(this::writerXmlChunk);
        channel.writerNewline("</mapper>");
    }
    /**
     * 写入xml块内容
     */
    private void writerXmlChunk(MethodInfo method){
        channel.writerEmptyLine();
        String mn = method.getMethodName();
        if (mn.startsWith(MAPPER_SELECT)){
            writerXmlChunkSelect(method);
        }
        if (mn.startsWith(MAPPER_UPDATE)){
            writerXmlChunkUpdate(method);
        }
        if (mn.startsWith(MAPPER_DELETE)){
            writerXmlChunkDelete(method);
        }
        if (mn.startsWith(MAPPER_INSERT)){
            writerXmlChunkInsert(method);
        }
    }
    private void writerXmlChunkInsert(MethodInfo method) {
        StringBuilder start = new StringBuilder("\t<insert ");
        start.append(" id=\"%s\"".formatted(method.getMethodName()));
        start.append(" parameterType=\"%s\"".formatted(table.getTableLowerName()));
        start.append(">");
        channel.writerNewline(start.toString());
        channel.writer("\t\tINSERT INTO %s(".formatted(table.getTableName()));
        channel.writer(getString(table.getColumns()));
        channel.writer(")");
        channel.writer("\n\t\tVALUES (");
        channel.writer(getInsertValues(table.getColumns()));
        channel.writerNewline(")");
        channel.writerNewline("\t</insert>");
    }
    private void writerXmlChunkDelete(MethodInfo method) {
        StringBuilder start = new StringBuilder("\t<update");
        start.append(" id=\"%s\"".formatted(method.getMethodName()));
        start.append(" parameterType=\"%s\"".formatted(getParamType(method.getParameter())));
        start.append(">");
        channel.writerNewline(start.toString());
        channel.writerNewline("\t\tupdate security_user set `delete` = true where id = #{id}");
        channel.writerNewline("\t</update>");
    }
    private void writerXmlChunkUpdate(MethodInfo method) {
        StringBuilder start = new StringBuilder("\t<update");
        start.append(" id=\"%s\"".formatted(method.getMethodName()));
        start.append(" parameterType=\"%s\"".formatted(table.getTableLowerName()));
        start.append(">");
        channel.writerNewline(start.toString());
        channel.writerNewline("\t\tUPDATE %s".formatted(table.getTableName()));
        channel.writer("\t\tSET ");
        channel.writerNewline(getUpdateSet(table.getColumns()));
        channel.writerNewline("\t\tWHERE id = #{id}");
        channel.writerNewline("\t</update>");
    }
    private void writerXmlChunkSelect(MethodInfo method) {
        StringBuilder start = new StringBuilder("\t<select");
        String rt = method.getReturnType();
        if (rt.contains(table.getTableUpperName())){
            rt = table.getTableLowerName();
        }
        start.append(" id=\"%s\"".formatted(method.getMethodName()));
        String paramType = getParamType(method.getParameter());
        if (paramType != null){
            start.append(" parameterType=\"%s\"".formatted(paramType));
        }
        start.append(" resultType=\"%s\"".formatted(rt));
        start.append(">");
        channel.writerNewline(start.toString());
        channel.writerNewline(createSelectSql(method, table.getColumns()));
        channel.writerNewline("\t</select>");
    }
    private String createSelectSql(MethodInfo method, List<Column> cs) {
        if (method.getMethodName().contains("Count")){
            return "\t\tselect count(*) from %s where `delete` = false"
                    .formatted(table.getTableName());
        }
        StringBuilder builder = new StringBuilder("\t\tselect ");
        String tempSql = getString(cs);
        builder.append(tempSql);
        builder.append("\n\t\tfrom ").append(table.getTableName());
        builder.append("\n\t\twhere `%s` = false".formatted(writer.getSqlLogicallyDelete()));
        List<String> parameter = method.getParameter();
        if (method.getMethodName().contains("Limit")){
            builder.append(" limit #{start}, #{end}");
        }else {
            for (String s : parameter) {
                builder.append(" and ");
                String p = s.split(" ")[1];
                builder.append(p).append(" = ");
                builder.append("#{").append(p).append("}");
            }
        }
        return builder.toString();
    }
    private String getString(List<Column> cs) {
        String tempSql = "";
        int maxLength = writer.getSqlLineMaxLength();
        for (int i = 0; i < cs.size(); i++) {
            String columnName = cs.get(i).getTableColumnName();
            if (checkFiled(columnName,writer.getSqlSelectIgnoreFields())){
                continue;
            }
            if (tempSql.length() > maxLength){
                tempSql += "\n\t\t\t\t";
                maxLength = maxLength + writer.getSqlLineMaxLength();
            }
            tempSql += ("`" + columnName + "`");
            if (i < cs.size() - 1) {
                tempSql += ", ";
            }
        }
        return tempSql;
    }
    private String getInsertValues(List<Column> cs) {
        String tempSql = "";
        int maxLength = writer.getSqlLineMaxLength();
        for (int i = 0; i < cs.size(); i++) {
            String columnName = cs.get(i).getTableColumnName();
            if (checkFiled(columnName,writer.getSqlSelectIgnoreFields())){
                continue;
            }
            if (tempSql.length() > maxLength){
                tempSql += "\n\t\t\t\t";
                maxLength = maxLength + writer.getSqlLineMaxLength();
            }
            tempSql += ("#{" + cs.get(i).getColumnName() + "}");
            if (i < cs.size() - 1) {
                tempSql += ", ";
            }
        }
        return tempSql;
    }
    private String getUpdateSet(List<Column> cs) {
        String tempSql = "";
        int maxLength = writer.getSqlLineMaxLength();
        for (int i = 0; i < cs.size(); i++) {
            String columnName = cs.get(i).getTableColumnName();
            if (checkFiled(columnName,writer.getSqlSelectIgnoreFields())){
                continue;
            }
            if (tempSql.length() > maxLength){
                tempSql += "\n\t\t\t\t";
                maxLength = maxLength + writer.getSqlLineMaxLength();
            }
            tempSql += ("`" + columnName + "`");
            tempSql += (" = ");
            tempSql += ("#{" + cs.get(i).getColumnName() + "}");
            if (i < cs.size() - 1) {
                tempSql += ", ";
            }
        }
        return tempSql;
    }
    private String getParamType(List<String> params) {
        if (params.isEmpty()){
            return null;
        }
        String param = params.getFirst().split(" ")[0];
        return StringUtil.firstLetterUpperCase(param);
    }
    private String getNamespace() {
        return project.getBaseName() + "." +
                project.getGroupName() + "." +
                project.getModuleName() + "." +
                project.getMapperPackageName() + "." +
                table.getTableUpperName() + StringUtil.firstLetterUpperCase(project.getMapperPackageName());
    }
    /**
     * 检查字段是否需要写入到xml中
     * @return 如果需要写入返回false
     */
    private boolean checkFiled(String filedName, String[] ignoreFields) {
        boolean exist = false;
        for (String ignoreField : ignoreFields) {
            if (filedName.equals(ignoreField)) {
                exist = true;
                break;
            }
        }
        return exist;
    }
}
